#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <windows.h>
#include <conio.h>//为了代码中的_getch（）函数
#include <math.h>
#include <time.h>  // 统一头文件至顶部，避免重复包含

#define FOREGROUND_INTENSITY 0x0008
// 全局常量（游戏地图尺寸、颜色定义）
#define MAP_WIDTH 60   // 地图宽度（字符数）
#define MAP_HEIGHT 20  // 地图高度（字符数）
#define INIT_SPEED 300 // 初始移动速度（ms）

// 全局变量（蛇、食物、得分等状态）
typedef struct {
    int x[100];  // 蛇身各段x坐标（最多100段）
    int y[100];  // 蛇身各段y坐标
    int len;     // 蛇长度
    int dir;     // 移动方向（1上/2下/3左/4右）
} Snake;

Snake snake;        // 蛇结构体
COORD food = {0,0}; // 食物坐标
int score = 0;      // 当前得分
int highest = 0;    // 最高分（从文件读取）
int sleeptime = INIT_SPEED; // 移动间隔时间（控制速度）
int gameover = 0;   // 游戏结束标志

// 函数前置声明（确保后续调用无编译警告）
  // 新增声明

// 控制台光标控制（隐藏光标，避免闪烁）
void HideCursor() {
    CONSOLE_CURSOR_INFO cursor_info = {1, 0}; //第二个值0为隐藏光标,光标大小为1表示高度百分比为1%
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);//将光标属性应用到控制台
}

// 设置控制台输入/输出为UTF-8编码（解决中文乱码）
void SetConsoleCodePage() {
    SetConsoleOutputCP(65001);  // 输出编码为UTF-8
    SetConsoleCP(65001);        // 输入编码为UTF-8
}

// 1.1 字符画：简单方块蛇（字符画装饰）
void printsnake() {
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hOut, FOREGROUND_RED | FOREGROUND_INTENSITY);
    printf("████████╗██╗  ██╗ ██████╗ ██████╗ ███████╗\n");
    printf("╚══██╔══╝██║  ██║██╔═══██╗██╔══██╗██╔════╝\n");
    printf("   ██║   ███████║██║   ██║██████╔╝█████╗  \n");
    printf("   ██║   ██╔══██║██║   ██║██╔══██╗██╔══╝  \n");
    printf("   ██║   ██║  ██║╚██████╔╝██║  ██║███████╗\n");
    printf("   ╚═╝   ╚═╝  ╚═╝ ╚═════╝ ╚═╝  ╚═╝╚══════╝\n");
    SetConsoleTextAttribute(hOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
}

// 1.2 欢迎界面菜单（字符蛇下方的菜单选择）
void welcometogame() {
    system("cls");
    printsnake();
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hOut, FOREGROUND_GREEN | FOREGROUND_INTENSITY);
    printf("\n\n\t\t1. 开始游戏\n");
    printf("\t\t2. 查看最高分\n");
    printf("\t\t3. 游戏说明\n");
    printf("\t\t4. 退出游戏\n");
    SetConsoleTextAttribute(hOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
    printf("\t\t请输入选项(1-4): ");
}

// 2.1 创建游戏地图（绘制游戏地图）
void createMap() {
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hOut, FOREGROUND_BLUE | FOREGROUND_INTENSITY);
    // 打印上下边框
    for(int i=0; i<MAP_WIDTH; i++) printf("■"); 
    printf("\n");
    // 打印中间边框（左右两侧）
    for(int i=1; i<MAP_HEIGHT-1; i++) {
        printf("■");
        for(int j=1; j<MAP_WIDTH-1; j++) printf(" ");
        printf("■\n");
    }
    // 打印下边框
    for(int i=0; i<MAP_WIDTH; i++) printf("■"); 
    SetConsoleTextAttribute(hOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
}

// 2.3 从文件读取最高分（从文件中读取最高分）
void File_out() {
    FILE *fp;
    fp = fopen("save.txt", "r");
    if(fp == NULL) {  // 文件不存在则初始化
        fp = fopen("save.txt", "w");
        fprintf(fp, "0");
        highest = 0;
    } else {
        fscanf(fp, "%d", &highest);
    }
    fclose(fp);
}

// 2.4 初始化蛇（绘制贪吃蛇）
void initsnake() {
    snake.len = 3;      // 初始长度3段
    snake.dir = 4;      // 初始方向：右
    // 初始位置：水平向右（蛇头在最右侧）
    for(int i=0; i<snake.len; i++) {
        snake.x[i] = MAP_WIDTH/2 - i;
        snake.y[i] = MAP_HEIGHT/2;
    }
}

// 2.5 创建随机食物（创建并随机出现食物）
void createfood() {
    srand((unsigned)time(NULL));
    while(1) {
        food.X = rand() % (MAP_WIDTH-2) + 1;  // 食物X坐标在1~MAP_WIDTH-2之间（避免在墙上）
        food.Y = rand() % (MAP_HEIGHT-2) + 1; // 食物Y坐标在1~MAP_HEIGHT-2之间
        int flag = 0;
        // 检查食物是否与蛇身重合
        for(int i=0; i<snake.len; i++) {
            if(food.X == snake.x[i] && food.Y == snake.y[i]) {
                flag = 1;
                break;
            }
        }
        if(flag == 0) break;  // 食物位置合法，退出循环
    }
}

// 3.1 判断是否咬到自己（判断蛇是否咬到自己）
int biteself() {
    for(int i=1; i<snake.len; i++) {
        if(snake.x[0] == snake.x[i] && snake.y[0] == snake.y[i]) {
            return 1;  // 咬到自己
        }
    }
    return 0;
}

// 3.2 判断是否撞墙（判断蛇是否撞到墙）
void cantcrosswall() {
    if(snake.x[0] <= 0 || snake.x[0] >= MAP_WIDTH-1 || 
       snake.y[0] <= 0 || snake.y[0] >= MAP_HEIGHT-1) {
        gameover = 1;  // 撞墙，游戏结束
    }
}

// 3.5 蛇自动移动（不按键时蛇自动前进）
void snakemove() {
    // 蛇身跟随：从尾部向前覆盖（最后一段消失，前段前移）
    for(int i=snake.len-1; i>0; i--) {
        snake.x[i] = snake.x[i-1];
        snake.y[i] = snake.y[i-1];
    }
    // 蛇头移动（根据方向更新坐标）
    switch(snake.dir) {
        case 1: snake.y[0]--; break;  // 上
        case 2: snake.y[0]++; break;  // 下
        case 3: snake.x[0]--; break;  // 左
        case 4: snake.x[0]++; break;  // 右
    }
}

// 3.6 键盘控制方向（键盘按键控制前进方向）
void keyboardControl() {
    if(GetAsyncKeyState(VK_UP) && snake.dir != 2)    snake.dir = 1;  // 上（不能反向）
    else if(GetAsyncKeyState(VK_DOWN) && snake.dir != 1) snake.dir = 2;  // 下
    else if(GetAsyncKeyState(VK_LEFT) && snake.dir != 4) snake.dir = 3;  // 左
    else if(GetAsyncKeyState(VK_RIGHT) && snake.dir != 3) snake.dir = 4; // 右
    else if(GetAsyncKeyState(VK_ESCAPE)) gameover = 1;  // ESC退出
    // 加速（F1）和减速（F2）（对应需求3.3/3.4）
    if(GetAsyncKeyState(VK_F1) && sleeptime > 10) {
        sleeptime -= 10;
        score += 2;
    }
    if(GetAsyncKeyState(VK_F2) && sleeptime < 350) {
        sleeptime += 30;
        score -= 2;
    }
}

// 游戏主循环（整合所有功能）
void startgame() {
    system("cls");
    File_out();       // 读取最高分
    createMap();      // 绘制地图
    initsnake();      // 初始化蛇
    createfood();     // 生成食物
    HideCursor();     // 隐藏光标

    while(!gameover) {
        // 绘制蛇和食物（每次循环刷新）
        SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), food);
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN);
        printf("●");  // 食物用绿色●表示

        for(int i=0; i<snake.len; i++) {
            COORD pos = {snake.x[i], snake.y[i]};
            SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
            SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN);
            printf("★");  // 蛇身用黄色★表示
        }

        keyboardControl();  // 监听键盘
        snakemove();        // 蛇移动
        cantcrosswall();    // 检查撞墙
        if(biteself()) gameover = 1;  // 检查咬自己

        // 检查是否吃到食物
        if(snake.x[0] == food.X && snake.y[0] == food.Y) {
            snake.len++;    // 蛇长度+1
            score += 10;    // 得分+10
            createfood();   // 生成新食物
        }

        Sleep(sleeptime);   // 控制移动速度
        system("cls");      // 清屏刷新
        createMap();        // 重新绘制地图（避免蛇移动后残留）
    }

    // 游戏失败界面（添加输入验证）
    system("cls");  //游戏结束部分
    printf("\n\n\t\t游戏结束！得分：%d\n", score);
    if(score > highest) {
        printf("\t\t创纪录啦！最高分被刷新为：%d\n", score);
        FILE *fp = fopen("save.txt", "w");
        fprintf(fp, "%d", score);
        fclose(fp);
    } else {
        printf("\t\t继续努力吧~ 离最高分还差：%d\n", highest - score);
    }
    printf("\t\t1.重玩\t2.退出\n");
    
    while (_kbhit()) {
        _getch();  // 读取并丢弃所有残留按键
    }
    // 新增输入验证循环（确保用户只能输入1或2）
    int choice;
    do {
        printf("\t\t请输入选项(1-2): ");
        choice = _getch();
    } while(choice != '1' && choice != '2');
    if(choice == '1') {
        gameover = 0;
        score = 0;
        sleeptime = INIT_SPEED;
        startgame();  // 重玩
    } else {
        return;      // 退出
    }
}

// 主函数（程序入口）
int main() {
    HideCursor();        // 隐藏光标
    SetConsoleCodePage();// 设置控制台编码为UTF-8
    while(1) {
        welcometogame();  // 显示欢迎界面
        int choice = _getch();
        switch(choice) {
            case '1': startgame(); break;      // 开始游戏
            case '2': File_out(); printf("\n\t\t当前最高分：%d\n", highest); _getch(); break; // 查看最高分
            case '3': printf("\n\t\t操作说明：方向键控制移动，F1加速，F2减速，ESC退出\n"); _getch(); break; // 游戏说明
            case '4': exit(0); break;          // 退出
            default: printf("\n\t\t输入错误！按任意键返回..."); _getch(); break;
        }
    }
    return 0;
}